13. Exercise: Display Images in a Grid

L8 22 Displaying A Grid Of Internet Images HS-SC

Now it's your turn! If you want to start at this step, you can download this exercise from: Step.05-Exercise-Displaying-a-Grid-of-Internet-Images. You will find plenty of helpful //TODO comments there.

Your app shows you just a single property on Mars. Let's give you some real-estate choices!

When you've completed with this exercise you'll be able scroll through whole list of properties in a new RecyclerView.

  1. Add the Gradle dependency for the RecyclerView:
implementation "androidx.recyclerview:recyclerview:$version_recyclerview" 



  1. In OverviewViewModel rename _property to _properties, and assign it a List of MarsProperty:

    private val _properties = MutableLiveData<List<MarsProperty>>()
    
    val properties: LiveData<List<MarsProperty>>
       get() = _properties


  2. Then update getMarsRealEstateProperties() to return the entire list instead of just one item:

_properties.value = listResult


  1. In grid_view_item.xml replace the viewModel variable with a property of type MarsProperty:
 <variable
    name="property"           
    type="com.example.android.marsrealestate.network.MarsProperty" />

Then update the binding in mars_image to reflect the variable change:

 app:imageUrl="@{property.imgSrcUrl}"



  1. In OverviewFragment inflate a FragmentOverviewBinding instead of a GridViewItemBinding:
val binding = FragmentOverviewBinding.inflate(inflater) 


  1. In fragment_overview, replace the TextView with a RecyclerView, using a GridLayoutManager to display items in a 2-column grid:
 <androidx.recyclerview.widget.RecyclerView
     android:id="@+id/photos_grid"
     android:layout_width="0dp"
     android:layout_height="0dp"
     android:padding="6dp"
     app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
     app:layout_constraintBottom_toBottomOf="parent"
     app:layout_constraintLeft_toLeftOf="parent"
     app:layout_constraintRight_toRightOf="parent"
     app:layout_constraintTop_toTopOf="parent"
     app:spanCount="2"
     tools:itemCount="16"
     tools:listitem="@layout/grid_view_item" /> 



Previewing the overview_fragment in the Design panel now shows a grid of images. Let's implement the RecyclerView Adapter in our app to show our list of Mars rover photos.

  1. In PhotoGridAdapter.kt create a 'PhotoGridAdapter' class that extends a RecyclerView ListAdapter with DiffCallback. Have it use a custom PhotoGridAdapter.MarsPropertyViewHolder to present a list of <MarsProperty> objects:
class PhotoGridAdapter : ListAdapter<MarsProperty, PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {}


  1. Add the imports for the ListAdapter and MarsProperty. Make sure to import androidx.recyclerview.widget.ListAdapter.
 import androidx.recyclerview.widget.ListAdapter
 import com.example.android.marsrealestate.network.MarsProperty


  1. Use Control-I to have Android Studio override the adapter's required onCreateViewHolder() and onBindViewHolder() methods: Don't bother filling them out yet.
 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
 PhotoGridAdapter.MarsPropertyViewHolder {
    TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
 }

 override fun onBindViewHolder(holder: PhotoGridAdapter.MarsPropertyViewHolder, position: Int) {
    TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
 }


  1. Create the DiffCallback companion object and override its two required areItemsTheSame() methods:
 companion object DiffCallback : DiffUtil.ItemCallback<MarsProperty>() {
    override fun areItemsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
       return oldItem === newItem
    }

    override fun areContentsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
       return oldItem.id == newItem.id
    }
 }


  1. Create a MarsPropertyViewHolder inner class, and implement the bind() method that includes a binding to marsProperty:

    Hint Don't forget to call binding.executePendingBindings()!

 class MarsPropertyViewHolder(private var binding: GridViewItemBinding):
    RecyclerView.ViewHolder(binding.root) {
       fun bind(marsProperty: MarsProperty) {
          binding.property = marsProperty
          binding.executePendingBindings()
       }
     }


  1. Implement the empty onCreateViewHolder() and onBindViewHolder() methods:
 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoGridAdapter.MarsPropertyViewHolder {
    return MarsPropertyViewHolder(GridViewItemBinding.inflate(LayoutInflater.from(parent.context)))
 }

 override fun onBindViewHolder(holder: PhotoGridAdapter.MarsPropertyViewHolder, position: Int) {
    val marsProperty = getItem(position)
    holder.bind(marsProperty)
 }


  1. In BindingAdapters, add a bindRecyclerView binding adapter for listData, and have it call submitList() on the PhotosGridAdapter:

    @BindingAdapter("listData")
    fun bindRecyclerView(recyclerView: RecyclerView, data: List<MarsProperty>?) {
        val adapter = recyclerView.adapter as PhotoGridAdapter
        adapter.submitList(data)
    }


  2. In fragment_overview, in photos_grid, bind the listData binding adapter to viewModel.properties:

app:listData="@{viewModel.properties}"


  1. In OverviewFragment, set the adapter in the RecyclerView (the photosGrid.adapter in the binding object) to a new PhotoGridAdapter

    binding.photosGrid.adapter = PhotoGridAdapter()

  2. In fragment_overview, add an attribute to the RecyclerView to set clipToPadding to false:

    android:clipToPadding="false"

  3. And, you made it! Build and run the app to see the grid of properties --- so much more choice!

If you get stuck, go back and watch the video again. Once you’re done, you can check your solution against the solution we’ve provided here: Step.05-Solution-Displaying-a-Grid-of-Internet-Images, or using this git diff.

Task Description:

Complete the tasks below to display your images in a grid.

Task List:

Task Feedback:

Great job! So many properties to choose from!